package com.cloud.common.support;

import com.cloud.common.context.AppContext;
import com.cloud.common.exception.AppException;
import com.cloud.common.util.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.concurrent.Callable;

/**
 * 拦截器父类
 *
 * @author fengwenjin
 */
@Slf4j
public class BaseInterceptor {

    public Object execute(ProceedingJoinPoint point, Callable callable) throws Throwable {
        // 入参数据
        logArgs(point);

        long beginTime = System.currentTimeMillis();
        Object result = null;
        try {
            // 调用方法
            if (callable == null) {
                result = point.proceed();
            } else {
                result = callable.call();
            }
            return result;
        } catch (Throwable e) {
            logError(point, e);
            throw e;
        } finally {
            // 结果数据
            logResult(point, result, beginTime);
        }
    }

    public String getArgs(ProceedingJoinPoint point) {
        StringBuilder params = new StringBuilder();
        Object[] argValues = point.getArgs();
        if (argValues != null && argValues.length > 0) {
            MethodSignature methodSignature = (MethodSignature) point.getSignature();
            String[] argNames = methodSignature.getParameterNames();
            for (int i = 0; i < argValues.length; i++) {
                if (argValues[i] == null) {
                    params.append(argNames[i] + "=null");
                } else {
                    params.append(argNames[i] + "=" + argValues[i]);
                }
                params.append(" ");
            }
        }
        return params.toString();
    }

    private Logger getLogger(ProceedingJoinPoint point) {
        try {
            Field field = point.getSignature().getDeclaringType().getDeclaredField("log");
            if (Modifier.isStatic(field.getModifiers())) {
                field.setAccessible(true);
                return (Logger) field.get(point.getTarget());
            }
        } catch (Exception e) {
        }
        return log;
    }

    private void logError(ProceedingJoinPoint point, Throwable e) {
        if (e instanceof AppException) {
            return;
        }
        String message = point.getSignature().toShortString() + " execute failure, params >>> " + getArgs(point);
        getLogger(point).error(message);
    }

    private void logArgs(ProceedingJoinPoint point) {
        Logger logger = getLogger(point);
        if (AppContext.isProdEnv() && !logger.isDebugEnabled()) {
            return;
        }
        try {
            String message = point.getSignature().toShortString() + " params >>> {}";
            if (logger.isDebugEnabled()) {
                logger.debug(message, getArgs(point));
            } else {
                logger.info(message, getArgs(point));
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }

    private void logResult(ProceedingJoinPoint point, Object result, long beginTime) {
        Logger logger = getLogger(point);
        if (AppContext.isProdEnv() && !logger.isDebugEnabled()) {
            return;
        }
        try {
            String message = point.getSignature().toShortString() + " costTime={} result >>> {}";
            String resultData = JsonUtils.toJSONString(result);
            long costTime = System.currentTimeMillis() - beginTime;
            if (costTime >= 500) {
                logger.warn(message, costTime, resultData);
            } else if (logger.isDebugEnabled()) {
                logger.debug(message, costTime, resultData);
            } else {
                logger.info(message, costTime, resultData);
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }
}
